part of internal;

class Buffer {
   Line _first, _here;
   int _recent, _lncnt;

   // CONSTRUCTORS \\

   Buffer.empty () {
      _first = new Line('');
      _recent = 0;
      _here = first;
      _lncnt = 1;
   }
   // this one is long and complex, couldn't have made it simpler
   Buffer.fromFile (String file) {
      Line curr;
      int from = 0;
      for (int i = 0; i < file.length; i++) {
         if (file[i] != '\n') continue;
         String temp = file.substring(from, i);
         if (_first == null) {
            _first = new Line(temp);
            _recent = 0;
            _here = first;
            _lncnt = 1;
            curr = first;
         }
         else {
            curr.insertAfter(new Line(temp));
            _lncnt++;
            curr = curr.next;
         }
         from = i + 1;
      }
      if (from == file.length) {
         curr.insertAfter(new Line(''));
         _lncnt++;
      }
      else {
         String temp = file.substring(from);
         if (_first == null) {
            _first = new Line(temp);
            _recent = 0;
            _here = first;
            _lncnt = 1;
         }
         else {
            curr.insertAfter(new Line(temp));
            _lncnt++;
         }
      }
   }

   // PRIVATE \\

   Line _getLine (int which) {
      if (which > recent) for (int i = recent; i < which; i++) here = here.next;
      if (recent > which) for (int i = which; i < recent; i++) here = here.prev;
      recent = which;
      return here;
   }

   // PUBLIC \\

   // write operations
   void inssq (Position where, String seq) {
      Line curr = _getLine(where.line);
      String left = curr.chars.substring(0, where.char);
      String right = curr.chars.substring(where.char);
      curr.chars = left + seq + right;
   }
   void insnl (Position where) {
      Line curr = _getLine(where.line);
      String left = curr.chars.substring(0, where.char);
      String right = curr.chars.substring(where.char);
      curr.chars = left;
      curr.insertAfter(new Line(right));
   }
   void delsq (int seq, Position where) {
      Line curr = _getLine(where.line);
      String left = curr.chars.substring(0, where.char - seq);
      String right = curr.chars.substring(where.char);
      curr.chars = left + right;
   }
   void delnl (Position where) {
      Line curr = _getLine(where.line);
      String left = curr.prev.chars;
      String right = curr.chars;
      curr.prev.chars = left + right;
      _getLine(where.line - 1); // to maintain 'recent' and 'here' invariant
      curr.remove();
   }

   // read operations
   String getch (Position where, int cnt) {
      StringBuffer temp = new StringBuffer();
      Line curr = _getLine(where.line);
      String str = curr.chars.substring(where.char);
      while (true) {
         if (cnt > str.length) {
            temp.write(str);
            temp.writeln();
            cnt -= str.length;
            cnt--;
         }
         else {
            temp.write(str.substring(0, cnt));
            break;
         }
         curr = curr.next;
         str = curr.chars;
      }
      return temp.toString();
   }
   String getln (int which) {
      Line curr = _getLine(which);
      return curr.chars;
   }
   bool isFirst (int which) => _getLine(which).prev == null;
   bool isLast (int which) => _getLine(which).next == null;

   // PUBLIC \\

   // write operations
   void inssq (Position where, String seq);
   void insnl (Position where);
   void delsq (Position where, int seq);
   void delnl (Position where);

   // read operations
   String getch (Position where, int cnt);
   String getln (int which);
   int get lncnt;

   // STRING \\

   String toString () {
      StringBuffer temp = new StringBuffer();
      for (Line curr = first; curr != null; curr = curr.next) {
         temp.write(curr.chars);
         if (curr.next != null) temp.writeln();
      }
      return temp.toString();
   }

}

class Line {
   String _chars;
   Line _prev, _next;

   // CONSTRUCTORS \\

   Line (this._chars) {
      if (_chars == null) throw null_arg;
   }

   // PUBLIC \\

   get String chars { return _chars; }
   set String chars (String 

   // returns 'this' line
   Line remove () {
      Line before = this.prev;
      Line after = this.next;
      if (before == null && after == null) {
         return this;
      }
      if (before == null) {
         after.prev = null;
         return this;
      }
      if (after == null) {
         before.next = null;
         return this;
      }
      before.next = after;
      after.prev = before;
      return this;
   }

   void insertBefore (Line that) {
      if (that == null) throw null_arg;
      Line temp = this.prev;
      this.prev = that;
      that.prev = temp;
      that.next = this;
      if (temp != null) temp.next = that;
   }

   void insertAfter (Line that) {
      if (that == null) throw null_arg;
      Line temp = this.next;
      this.next = that;
      that.next = temp;
      that.prev = this;
      if (temp != null) temp.prev = that;
   }
}
